package com.ocean.core.util.upload;

import com.ocean.core.exception.verify.ApplicationException;
import com.ocean.core.model.vo.VideoUploadVo;
import com.ocean.core.util.upload.properties.VideoProperties;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.*;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * 视频操作类
 *
 * @author zhang
 * @since 2023/10/27
 */
@Component
@Slf4j
public class VideoUtil {

    private static VideoProperties videoProperties;

    public VideoUtil(VideoProperties videoProperties) {
        VideoUtil.videoProperties = videoProperties;
    }


    /**
     * 视频上传
     *
     * @param file 文件
     * @return 结果
     */
    public static VideoUploadVo uploadVideo(MultipartFile file) throws Exception {
        VideoUploadVo uploadVo = new VideoUploadVo();
        if (file.isEmpty()) {
            log.warn("上传文件为空，跳过...");
            throw new ApplicationException("上传文件为空");
        }
        String rename = file.getOriginalFilename();
        if (rename == null) {
            throw new ApplicationException("请上传一个有名字的文件");
        }
        LocalDate now = LocalDate.now();
        // 转换时间格式
        String format = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(now);
        // 上传到指定磁盘目录
        String diskLocation = videoProperties.getUpload() + File.separator + format + File.separator;
        String sourceFolder = File.separator + format + File.separator;
        // 后缀
        String suffix = rename.substring(rename.lastIndexOf("."));
        String newFileName = getUuid();
        // 完整文件名
        String fileName = newFileName + suffix;
        // 最终目录
        String lastFilePath = diskLocation + fileName;
        //封面目录
        String coverLastFilePath = diskLocation + File.separator + newFileName + ".png";
        //预览视频存放位置
        String previewDiskLaseLocation = diskLocation + newFileName + "-PREVIEW" + ".mp4";
        File filePath = FileUtil.createFilePath(lastFilePath);
        // 可访问的路径
        String coverAddress = videoProperties.getRequestUrl() + sourceFolder + newFileName + ".png";
//        String coverAddress = videoProperties.getRequestUrl() + sourceFolder + newFileName + suffix;
        log.info("文件完整路径: " + filePath);
        try {
            //上传还是直接上传在容器中
            file.transferTo(filePath);
            //设置封面
            fetchFrame(filePath.toString(), coverLastFilePath, videoProperties.getWidth(), videoProperties.getHeight());
            //截取视频预览
            clipExtractor(filePath.toString(), previewDiskLaseLocation);
            uploadVo.setRename(rename);
            uploadVo.setDiskLocation(filePath.toString());
            //仅仅做一个路径拼接，实际封面图片由视频截取
            uploadVo.setCoverUrl(coverAddress.replaceAll("\\\\", "/"));
            uploadVo.setPreviewDiskLocation(previewDiskLaseLocation);
            uploadVo.setVideoLength(getVideoDurationInMinutes(filePath.toString()));
        } catch (IOException e) {
            log.error("文件上传出现错误：", e.fillInStackTrace());
        }
        return uploadVo;
    }

    /**
     * 截取视频预览
     *
     * @param filePath 源视频位置
     * @param output   预览视频位置
     */
    public static void clipExtractor(String filePath, String output) {
        FrameGrabber grabber = new FFmpegFrameGrabber(filePath);
        try {
            grabber.start();

            // 设置截取的起始时间
            double startTime = 0;
            // 设置截取的时长（秒）
            double duration = 20;

            // 计算截取的帧数
            int startFrame = (int) (startTime * grabber.getFrameRate());
            int endFrame = (int) ((startTime + duration) * grabber.getFrameRate());
            // 设置帧数范围
            grabber.setFrameNumber(startFrame);
            // 创建输出视频文件的编码器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(output, grabber.getImageWidth(), grabber.getImageHeight());
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setFormat("mp4");
            recorder.setAudioChannels(1);

            // 设置编码速度，可选参数包括：ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow
            recorder.setVideoOption("preset", "ultrafast");
            // 控制视频质量，0为无损压缩，通常范围为18-28
            recorder.setVideoOption("crf", "28");
            // 设置视频比特率，500kb
            recorder.setVideoBitrate(200 * 1024);
            recorder.setVideoQuality(0.1);
            recorder.start();
            Frame frame;
            int frameCount = 0;

            while ((frame = grabber.grab()) != null && frameCount < (endFrame - startFrame)) {
                // 将帧写入输出视频文件
                recorder.record(frame);
                frameCount++;
            }
            grabber.stop();
            recorder.stop();
        } catch (Exception e) {
            log.error("截取视频预览失败：", e.fillInStackTrace());
        }
    }

    /**
     * 获取视频分钟数
     *
     * @param videoPath 视频地址
     * @return 分钟
     */
    public static int getVideoDurationInMinutes(String videoPath) {
        int durationInMinutes = 0;

        try {
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoPath);
            grabber.start();

            int durationInFrames = grabber.getLengthInFrames();
            int frameRate = (int) Math.round(grabber.getFrameRate());
            int durationInSeconds = durationInFrames / frameRate;
            durationInMinutes = durationInSeconds / 60;

            grabber.stop();
            grabber.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return durationInMinutes;
    }

    /**
     * 截取视频封面
     *
     * @param videoFile 视频路径
     * @param frameFile 保存的图片完整路径
     * @param width     指定的宽度
     * @param height    指定的高度
     * @throws Exception Exception
     */
    public static void fetchFrame(String videoFile, String frameFile, int width, int height)
            throws Exception {
        long start = System.currentTimeMillis();
        File targetFile = new File(frameFile);
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoFile);
        ff.start();
        int length = ff.getLengthInFrames();
        int i = 0;
        Frame f = null;
        while (i < length) {
            // 去掉前3000帧，避免出现全黑的图片，依自己情况而定
            f = ff.grabImage();
            if ((i > 3000) && (f.image != null)) {
                break;
            }
            i++;
        }
        // 缩放图片
        BufferedImage scaledImage = scaleImage(frameToBufferedImage(f), width, height);

        ImageIO.write(scaledImage, "jpg", targetFile);
        ff.stop();
        ff.close();
        log.info("封面生成耗时：{}", (System.currentTimeMillis() - start) / 1000 + "秒");
    }

    /**
     * 缩放图片
     *
     * @param originalImage 原始图片
     * @param width         指定的宽度
     * @param height        指定的高度
     * @return 缩放后的图片
     */
    public static BufferedImage scaleImage(BufferedImage originalImage, int width, int height) {
        BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2D = scaledImage.createGraphics();
        graphics2D.drawImage(originalImage, 0, 0, width, height, null);
        graphics2D.dispose();
        return scaledImage;
    }

    public static BufferedImage frameToBufferedImage(Frame frame) {
        //创建BufferedImage对象
        Java2DFrameConverter converter = new Java2DFrameConverter();
        return converter.getBufferedImage(frame);
    }

    public static String getUuid() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }


}
